package org.jeecg.common.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.util.CollectionUtils;

import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;

/**
 * 流工具类
 *
 * @author zjarlin
 * @since 2022/4/27 10:53 PM
 */
@SuppressWarnings({"javadoc", "unused"})
public interface Streams {

    public static <T, R> Map<Integer, List<T>> partition(Collection<T> collection, Function<T, R> getFun, Predicate<R>... predicates) {
        return collection.stream().collect(Collectors.groupingBy(t -> IntStream.range(0, predicates.length)
            .filter(i -> predicates[i].test(getFun.apply(t)))
            .findFirst()
            .orElse(-1)));
    }

    public static <T, R> Map<Integer, List<T>> partition2(Collection<T> collection, Function<T, R> getFun, Predicate<R>... predicates) {
        // 将满足谓词的元素分区,不满足的Map的key都归为-1,满足的key是该谓词下标

        Map<Integer, List<T>> result = new HashMap<>();

        // initialize the result map with empty lists for each predicate index
        for (int i = 0; i < predicates.length; i++) {
            result.put(i, new ArrayList<>());
        }

        result.put(-1, new ArrayList<>());

        collection.stream()
            .map(elem -> new AbstractMap.SimpleImmutableEntry<>(
                IntStream.range(0, predicates.length)
                    .filter(i -> predicates[i].test(getFun.apply(elem)))
                    .findFirst()
                    .orElse(-1),
                elem))
            .collect(Collectors.groupingBy(Map.Entry::getKey,
                Collectors.mapping(Map.Entry::getValue, Collectors.toList())))
            .forEach(result::put);

        return result;
    }

    static <T> Triple<List<T>, List<T>, List<T>> compare(Collection<T> news, Collection<T> olds, Function<T, ?> newsK, Function<T, ?> oldsK) {
        return compare(news, olds, newsK, oldsK, Function.identity());
    }

    /**
     * 比较集合进行增删改
     *
     * @param news  前端传过来的集合
     * @param olds  后端查出来的集合
     * @param newsK newsk 主键getFun
     * @param oldsK oldsk 主键getFun
     * @return {@link Triple }< {@link List }<{@link New }> ,{@link List }<{@link New }>,{@link List }<{@link New }> >
     * @author addzero
     * @since 2022/09/30
     */
    static <New, Old> Triple<List<Old>, List<Old>, List<Old>> compare(Collection<New> news, Collection<Old> olds, Function<New, ?> newsK, Function<Old, ?> oldsK, Function<List<New>, List<Old>> mapping) {
        final Set<?> newKs = news.stream().map(newsK).collect(toSet());//123          34
        final Set<?> oldKs = olds.stream().map(oldsK).collect(toSet());  //756               1235
        List<Old> add;
        List<Old> del;
        if (newKs.size() >= oldKs.size()) {
            //需要删除
            del = mapping.apply(getGenericDiffs(news, olds, newsK, oldsK));
            add = getGenericDiffs(olds, news, oldsK, newsK);
        } else {
            //需要删除
            add = mapping.apply(getGenericDiffs(news, olds, newsK, oldsK));
            del = getGenericDiffs(olds, news, oldsK, newsK);
        }
        final List<Old> mod = mapping.apply(getGenericIntersection(news, olds, newsK, oldsK));
        return Triple.of(add, del, mod);
    }

    /**
     * 不同泛型差集A-B,以A的泛型返回
     *
     * @param as as
     * @param bs bs
     * @param aK a的主键
     * @param bK 汉堡王
     * @return 返回信息
     * @author zjarlin
     * @since 2022/06/29
     */
    static <A, B> List<A> getGenericDiffs(Collection<A> as, Collection<B> bs, Function<A, ?> aK, Function<B, ?> bK) {
        final Set<?> aks = as.stream().map(aK).collect(toSet());
        final Set<?> bks = bs.stream().map(bK).collect(toSet());
        final Set<?> diffset = aks.stream().filter(a -> !bks.contains(a)).collect(toSet());
        final List<A> collect = as.stream().filter(a -> diffset.contains(aK.apply(a))).collect(toList());
        return Optional.of(collect).filter(CollUtil::isNotEmpty).orElseGet(ArrayList::new);
    }

    /**
     * 交集A&B,以A的泛型返回
     *
     * @param as as
     * @param bs bs
     * @param aK agetFun主键
     * @param bK b外键
     * @return 返回信息
     * @author zjarlin
     * @since 2022/06/29
     */

    static <A, B> List<A> getGenericIntersection(Collection<A> as, Collection<B> bs, Function<A, ?> aK, Function<B, ?> bK) {
        if (CollUtil.isEmpty(bs)) {
            return (List<A>) as;
        }
        final Set<?> aks = as.stream().map(aK).collect(toSet());
        final Set<?> bks = bs.stream().map(bK).collect(toSet());
        final Set<?> diffset = aks.stream().filter(bks::contains).collect(toSet());
        final List<A> collect = as.stream().filter(a -> diffset.contains(aK.apply(a))).collect(toList());
        return Optional.of(collect).filter(CollUtil::isNotEmpty).orElseGet(ArrayList::new);
    }

    static <Old> Triple<List<Old>, List<Old>, List<Old>> compare(Collection<Old> news, Collection<Old> olds) {
        return compare(news, olds, Function.identity(), Function.identity(), Function.identity());
    }

    static <T, K> K isInMultiMapValuesThenFindKey(Collection<?> source, Map<K, ? extends Collection<T>> map, Function<T, ?> getFun) {
        return isInMultiMapValuesThenFindKeys(source, map, getFun).stream().findAny().orElse(null);
    }

    /**
     * 根据map的value(List<T>子集source)获取map的keys
     * map的value是个list,判断source是不是list<?>的子集</>
     * 如果是,return keys,否则返回null;
     *
     * @param source 前端穿过来若干个?
     * @param map    一对多map
     * @param getFun map的v的getFun
     * @return map的key
     * @author zjarlin
     * @since 2022/5/27 9:16 PM
     */
    static <T, K> List<K> isInMultiMapValuesThenFindKeys(Collection<?> source, Map<K, ? extends Collection<T>> map, Function<T, ?> getFun) {
        final ArrayList<K> ret = new ArrayList<>();
        final Set<?> nonNullSource = source.stream().filter(Objects::nonNull).collect(Collectors.toSet());
        map.forEach((k, v) -> {
            final Set<?> collect = v.stream().filter(Objects::nonNull).map(getFun).filter(Objects::nonNull).collect(Collectors.toSet());
            if (collect.contains(nonNullSource)) {
                ret.add(k);
            }
        });
        return ret;
    }

    /**
     * 根据多个字段去重
     *
     * @param body   身体
     * @param getFun 得到乐趣
     * @return 返回信息
     * @author zjarlin
     * @since 2022/06/29
     */
    @SafeVarargs
    static <T> List<T> unique(Collection<T> body, Function<T, ?>... getFun) {
        return body.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(e -> {
            final StringBuilder stringBuilder = new StringBuilder();
            String s = null;
            for (final Function<T, ?> tFunction : getFun) {
                final Object apply = tFunction.apply(e);
                final StringBuilder append = stringBuilder.append(ofNullable(apply).map(Object::toString).orElse(""));
                s = append.toString();
            }
            return s;
        }))), ArrayList::new));
    }

    /**
     * 得到同泛型差集
     *
     * @param as 作为
     * @param bs 废话
     * @return 返回信息
     * @author zjarlin
     * @since 2022/06/29
     */
    static <T> List<T> getGenericDiffs(Collection<T> as, Collection<T> bs) {
        return getGenericDiffs(as, bs, Function.identity(), Function.identity());
    }

    static <T> List<T> getGenericIntersection(Collection<T> as, Collection<T> bs) {
        return getGenericIntersection(as, bs, Function.identity(), Function.identity());
    }

    /**
     * 得到B-A的差集转为A泛型合入A集合中
     *
     * @param as              主表
     * @param bs              副表
     * @param aK              主表主键
     * @param bK              副表外键(可同名字段关联)
     * @param mapstructMethod mapstruct方法
     * @return 返回信息
     * @author zjarlin
     * @since 2022/06/29
     */
    static <A, B> List<A> aEatB(Collection<A> as, Collection<B> bs, Function<A, ?> aK, Function<B, ?> bK, Function<B, A> mapstructMethod) {
        final List<B> genericDiffs = getGenericDiffs(bs, as, bK, aK);
        final List<A> collect = genericDiffs.stream().map(mapstructMethod).collect(toList());
        return Stream.of(collect, as).flatMap(Collection::stream).distinct().collect(toList());
    }

    /**
     * leftjoin
     *
     * @param as              主表
     * @param bs              副表
     * @param aK              主键
     * @param bK              外键(主外键可同名)
     * @param mapstructMethod (传入mapstruct方法引用) 例如:R ab2r(A a,B b)
     * @return {@link List }<{@link R }>
     * @author zjarlin
     * @since 2022/06/29
     */
    static <A, B, R> List<R> leftjoin(Collection<A> as, Collection<B> bs, Function<A, ?> aK, Function<B, ?> bK, BiFunction<A, B, R> mapstructMethod) {
        if (CollUtil.isEmpty(as)) {
            return Collections.emptyList();
        }
        final List<R> fastret = as.stream().map(a -> mapstructMethod.apply(a, null)).filter(Objects::nonNull).distinct().collect(toList());
        if (CollUtil.isEmpty(bs)) {
            return fastret;
        }
        final List<A> genericIntersection = getGenericIntersection(as, bs, aK, bK);
        if (CollUtil.isEmpty(genericIntersection)) {
            return fastret;
        }
        //getGenericIntersection(collect, collect1);
        //final int asize = OptionalInt.of(as.size()).orElse(0);
        //final int bsize = OptionalInt.of(bs.size()).orElse(0);
        //if (asize > bsize) {return moreLeftjoinless(as, bs, aK, bK, mapstructMethod);}
        final Map<?, List<B>> relation = bs.stream().collect(groupingBy(bK));
        final List<A> genericDiffs = getGenericDiffs(as, bs, aK, bK);
        final List<R> ret = genericDiffs.stream().map(a1 -> mapstructMethod.apply(a1, null)).collect(Collectors.toList());
        final List<R> rs = Optional.of(as.stream().map(a -> {
            final Object ak = aK.apply(a);
            final List<R> external = ofNullable(ak).map(relation::get).orElse(Collections.emptyList()).stream().map(b -> mapstructMethod.apply(a, b)).collect(toList());
            ret.addAll(external);
            return Collections.unmodifiableList(ret);
        }).flatMap(Collection::stream).filter(Objects::nonNull).distinct().collect(toList())).filter(CollUtil::isNotEmpty).orElseGet(ArrayList::new);
        if (CollUtil.isEmpty(rs)) {
            return fastret;
        }
        rs.removeIf(Objects::isNull);
        return rs;
    }

    /**
     * leftjoinless
     *
     * @param as
     * @param bs
     * @param aK
     * @param bK
     * @param mapstructMethod mapstruct方法
     * @return {@link List }<{@link R }>
     * @author zjarlin
     * @since 2022/06/20
     */
    static <A, B, R> List<R> moreLeftjoinless(Collection<A> as, Collection<B> bs, Function<A, ?> aK, Function<B, ?> bK, BiFunction<A, B, R> mapstructMethod) {
        if (CollectionUtils.isEmpty(bs)) {
            return (List<R>) as;
        }
        final Map<?, B> relation = bs.stream().collect(toMap(bK, Function.identity()));
        return as.stream().map(a -> ofNullable(aK.apply(a))
            .map(relation::get)
            .map(b -> mapstructMethod.apply(a, b))
            .orElseGet(() -> mapstructMethod.apply(a, null))).filter(Objects::nonNull).collect(toList());
    }

    /**
     * 一步一判空去重的流操作
     *
     * @param coll     原集合
     * @param funChain 函数链
     * @return {@link Stream }<{@link R }>
     * @author zjarlin
     * @since 2022/06/29
     */
    @SafeVarargs
    static <T, R> Stream<R> opt(Collection<T> coll, Function<? super T, ? extends R>... funChain) {
        Stream<R> ret = opt(coll, funChain[0]);
        for (int i = 1; i < funChain.length; i++) {
            ret = opt((Collection<T>) ret.collect(toList()), funChain[i]);
        }
        return ret;
    }

    /**
     * 一步一判空去重的流操作
     *
     * @param coll 原集合
     * @param fun  函数
     * @return {@link Stream }<{@link R }>
     * @author zjarlin
     * @since 2022/07/23
     */
    static <T, R> Stream<R> opt(Collection<T> coll, Function<? super T, ? extends R> fun) {
        //首先对集合判空
        final Collection<T> ts = ofNullable(coll).filter(collection -> !CollectionUtils.isEmpty(collection)).orElseGet(ArrayList::new);
        //函数应用完再次判空,去重
        return (Stream<R>) ts.stream().filter(Objects::nonNull).map(fun).filter(Objects::nonNull).distinct();
    }

    /**
     * split指定多个分隔符
     *
     * @param s         要处理的字符串
     * @param separator 多个分隔符 入参
     * @return {@link List }<{@link String }>
     * @author addzero
     * @since 2022/11/10
     */
    static List<String> split4Separator(String s, String... separator) {
        Integer[] spliter = Arrays.stream(separator).map(s::indexOf).toArray(Integer[]::new);
        int separatorLastIndex = spliter.length - 1;

        List<String> collect = Stream.iterate(0, (index) -> ++index).limit(separatorLastIndex)
            .map(i -> StrUtil.sub(s, spliter[i], spliter[i + 1])).collect(toList());
        int strLastIndex = s.length();
        String sub = StrUtil.sub(s, spliter[separatorLastIndex], strLastIndex);
        collect.add(sub);
        return collect;
    }

}
